package Modele;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
/**
 * Classe permettant d'�crire dans un fichier une exp�rience torch. elle initialise le nn, les donn�es
 * @author philippestepniewski
 *
 */
public class Writer {
	/**
	 * M�thode initialisant le nn et inscrivant les lignes dans le fichier test. Au tout debut du fichier
	 * @param fichier : nom/chemin du fichier dans lequel on doit ecrire l'exp�rience
	 * @param input:nombre d'entr�es du nn
	 * @param output:nombre de sorties du nn
	 * @param param: tableau contenant les param�tre sur les couches cach�es du nn. Une case de l'arraylist contient le nombre de neuronne de HU
	 * @return
	 * @throws IOException
	 */
	public static boolean writeHead(File fichier, int input, int output, ArrayList<Integer> param) throws IOException
	{
		FileWriter f = new FileWriter(fichier,false);
		f.write("require \"lab\"\n");
		f.write("require \"torch\"\n");
		f.write("require \"nn\"\n");
		f.write("require \"nn.Piad\"\n");
		f.write("filer1 = torch.DiskFile(\"temp.lock\",\"rw\");\n");
		f.write("filer1:close()\n");
		f.write("piad=nn.Piad();\n");
		f.write("mlp = nn.Sequential();\n");
		f.write("mlp:add(nn.Linear("+input+", "+param.get(0)+"))\n");
		f.write("mlp:add(nn.Tanh())\n");
		if(param.size() == 1)
		{
			f.write("mlp:add(nn.Linear("+param.get(0)+","+output+"))\n");
		}else
		{
			for(int i = 0; i < param.size() - 1; i++)
			{
				f.write("mlp:add(nn.Linear("+param.get(i)+","+param.get(i+1)+"))\n");
				f.write("mlp:add(nn.Tanh())\n");
			}
			f.write("mlp:add(nn.Linear("+param.get(param.size() - 1)+","+output+"))\n");
		}
		f.write("\n");
		f.close();
		return true;
	}
        /**
         * Ecrit les lignes du fichier expérience corrspondant au chargement d'un fichier contenant un PMC
         * @param fichier le fichier expérience
         * @param name contenant le nom du PMC à charger
         * @return
         * @throws IOException
         */
	public static boolean writeHeadPMC(File fichier, String name) throws IOException
	{
		FileWriter f = new FileWriter(fichier,false);
		f.write("require \"lab\"\n");
		f.write("require \"torch\"\n");
		f.write("require \"nn\"\n");
		f.write("require \"nn.Piad\"\n");
		f.write("piad=nn.Piad();\n");
		f.write("filer1 = torch.DiskFile(\"temp.lock\",\"rw\");\n");
		f.write("filer1:close()\n");
		f.write("mlp = nn.Sequential();\n");
		f.write("filer1 = torch.DiskFile(\""+name+"\",\"rw\")\n");
		f.write("mlp:read(filer1)\n");
		f.write("filer1:close();\n");
		f.close();
		return true;
	}


        /**
         * Ecrit dans le fichier expérience les lignes correspondant au calcul des kmeans
         * @param fichier le fichier de l'expérience
         * @param listeCouche liste contenant le nombre de neurones dans chaque couche
         * @param k k des kmeans
         * @param nbiter nombres d'itération maximum du kmeans
         * @return
         * @throws IOException
         */
	public static boolean writeActivMatriceKmeans(File fichier,ArrayList<Integer> listeCouche,int k,int nbiter) throws IOException{
		FileWriter f = new FileWriter(fichier,true);
		for(int i=0;i<listeCouche.size();i++){
			f.write("TensorCouche"+i+"=torch.Tensor(app[1],"+listeCouche.get(i)+");\n");
		}

		f.write("for i=1,app[1] do\n");
		for(int i=0;i<listeCouche.size();i++){
			f.write("mlp:writeMatriceActivation(TensorCouche"+i+"[i],"+(i+1)+",app[2][i]);\n");
		}
		f.write("end\n");

		for(int i=0;i<listeCouche.size();i++){
			f.write("TensorCouche"+i+"=TensorCouche"+i+":transpose(1,2)"+";\n");
		}

		for(int i=0;i<listeCouche.size();i++){
			f.write("kpart"+i+"=piad:kparts(10,TensorCouche"+i+",app[1],2)"+";\n");
		}

		for(int i=0;i<listeCouche.size();i++){
			f.write("kmean"+i+"=piad:kmeans(kpart"+i+","+k+","+nbiter+");\n");
		}

		for(int i=0;i<listeCouche.size();i++){
			f.write("piad:writeOneKmeans("+5+",\"KmeansVect"+i+"\",kmean"+i+"[1],"+(i+1)+");\n");
		}
                for(int i=0;i<listeCouche.size();i++){
			f.write(" piad:writeYKmeans(\"Ykmean"+i+"\",kmean"+i+"[2])\n");
		}
		f.write("os.remove(\"temp.lock\");\n");
		f.close();
		return true;
	}
	/**
	 * M�thode �crivant les lignes de chargement des donn�es au format torch
	 * @param fichier fichier de l'experience
	 * @param sparse true si les donn�es sont en sparse, false sinon
	 * @param fichiers tableau des chemins des fichiers contenant les donn�es
	 * @param dim dimension des donn�es
	 * @param nbclasses nombre de classes possibles pour les donn�es
	 * @return
	 * @throws IOException
	 */
	public static boolean writeDataSet(File fichier,boolean sparse,ArrayList<String> fichiersData,int dim,int nbclasses) throws IOException
	{
		FileWriter f = new FileWriter(fichier,true);
		if(sparse){
			readOurSparse(dim,nbclasses,fichiersData, f);
		}
		else{
			readOur(dim,nbclasses,fichiersData,f);
		}
		f.write("\n");
                f.write("inside2={};\n");
		f.write("outside2={};\n");

		f.write("function inside2:size() return test[1] end\n");
		f.write("function outside2:size() return test[1] end\n");
		f.write("for i=1,inside2:size() do\n");
		f.write("local input2 = test[2][i]\n");
		f.write("local output2 = test[3][i]\n");
		f.write("inside2[i] = input2\n");
		f.write("outside2[i] = output2\n");
		f.write("end\n\n");
		f.write("dataset={};\n");
		f.write("inside={};\n");
		f.write("outside={};\n");
		f.write("function dataset:size() return app[1] end\n");
		f.write("function inside:size() return app[1] end\n");
		f.write("function outside:size() return app[1] end\n");
		f.write("for i=1,dataset:size() do\n");
		f.write("local input = app[2][i]\n");
		f.write("local output = app[3][i]\n");
		f.write("inside[i] = input\n");
		f.write("outside[i] = output\n");
		f.write("dataset[i] = {input, output}\n");
		f.write("end\n");
		f.write("\n");
		f.close();
		return true;
	}

	/**
	 * M�thode �crivant les ligne de chargement des donn�es d'apprentissage au format sparse
	 * @param dim : dimension des donn�es
	 * @param nbclasses : nombre de classes de donn�es possibles
	 * @param fichiers: chemin des fichiers apprentissage et test
	 * @param f: filewriter de la fonction dans laquelle la m�thode est appel�e
	 */
	private static void readOurSparse(int dim,int nbclasses,ArrayList<String> fichiers,FileWriter f){
		try {
			f.write("app = piad:ReadSparse(\""+fichiers.get(0)+"\","+dim+","+nbclasses+");\n");
			f.write("test = piad:ReadSparse(\""+fichiers.get(1)+"\","+dim+","+nbclasses+");\n");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	/**
	 * M�thode �crivant les ligne de chargement des donn�es d'apprentissage au format USPS
	 * @param dim : dimension des donn�es
	 * @param nbclasses : nombre de classes de donn�es possibles
	 * @param fichiers: chemin des fichiers apprentissage et test
	 * @param f: filewriter de la fonction dans laquelle la m�thode est appel�e
	 */
	private static void readOur(int dim,int nbclasses,ArrayList<String> fichiers,FileWriter f){
		try {
			f.write("app = piad:Read(\""+fichiers.get(0)+"\",\""+fichiers.get(1)+"\","+dim+","+nbclasses+");\n");
			f.write("test = piad:Read(\""+fichiers.get(2)+"\",\""+fichiers.get(3)+"\","+dim+","+nbclasses+");\n");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

        /**
         * Méthode écrivant les lignes permettant de sauvegarder un PMC
         * @param fichier le fichier de l'expérience
         * @param listecouche liste contenant le nombre de neurones dans chaque couche
         * @throws IOException
         */
	public static void writeSave(File fichier,ArrayList<Integer> listecouche) throws IOException
	{
		FileWriter f = new FileWriter(fichier,true);
                String nom=fichier.getName();

                if(listecouche!=null){
                    for(int i=0;i<listecouche.size();i++){
                        nom=nom+"_"+listecouche.get(i);
                    }
                }
		f.write("filer = torch.DiskFile(\"pmc." + nom+ "\",\"rw\");\n");
		f.write("mlp:write(filer)\n");
		f.write("filer:close()\n");

		f.close();
	}

        /**
         * Méthode qui écrit les lignes permettant de pré entrainer un pmc avec un auto encodeur
         * @param fichier le fichier de l'expérience
         * @throws IOException
         */
	public static void writeEncoder(File fichier) throws IOException
	{
		FileWriter f = new FileWriter(fichier,true);

		f.write("mlp:autoEncoder(dataset)\n");
		f.close();
	}

	/**
	 * Fonction ecrivant les lignes qui lancent l'apprentissage du r�seau de neurone
	 * @param fichier nom/chemin du fichier dans lequel on doit ecrire l'exp�rience
	 * @param learningRate epsilon, pas de la descente de gradient
	 * @return
	 * @throws IOException
	 */
	public static boolean writeTrain(File fichier,double learningRate) throws IOException
	{
		FileWriter f = new FileWriter(fichier,true);

		f.write("for i=1,50 do\n");
		f.write("mlp:writeActivation(\"a_"+ fichier.getName() +"_0_\" .. i ,test[2][i])\n");
		f.write("end\n\n");

		f.write("criterion = nn.MSECriterion()\n");
		f.write("local error = {}; \n");
		f.write("local cpt_error = 0\n");
		f.write("for i=1,2500 do\n");
		f.write("local z = math.random(app[1]);\n");
		f.write("local inp =  app[2][z]\n");
		f.write("local outp = app[3][z]\n");
		f.write("criterion:forward(mlp:forward(inp), outp)\n");
		f.write("mlp:zeroGradParameters()\n");
		f.write("mlp:backward(inp, criterion:backward(mlp.output, outp))\n");
		f.write("mlp:updateParameters(" + learningRate + ")\n\n");

		f.write("if i%500 == 0 then \n");
		f.write("cpt_error = cpt_error + 1\n");
		f.write("error[cpt_error] = piad:ErrBase(mlp,inside,outside)");
		f.write("for j=1,50 do\n");
		f.write("mlp:writeActivation(\"a_"+ fichier.getName() +"_\" .. i .. \"_\".. j ,test[2][j])\n");
		f.write("end\n");
		f.write("end\n\n");

		f.write("end\n\n");
		f.write("function error:size() return cpt_error end\n");
		f.write("piad:writeError(error,\"ErreurQuadra"+fichier.getName()+".txt\")\n\n");
		f.write("mlp:writeWeightAndBias(\"WeightAndBias.txt\")\n\n");
		f.write("piad:writeClassif(\"Classif_"+ fichier.getName() +".txt\",mlp,inside2,outside2)\n");
		f.write("for i=1,50 do\n");
		f.write("mlp:writeActivation(\"a_"+ fichier.getName() +"_3000_\" .. i ,test[2][i])\n");
		f.write("end\n");
		f.close();

		return true;
	}

        /**
         * Méthode qui permet d'écrire les lignes pour l'apprentissage avec un trainer Torch
         * @param fichier le fichier de l'experience
         * @param learningRate le pas de la descente de gradient
         * @return
         * @throws IOException
         */
	public static boolean writeTrain2(File fichier,double learningRate) throws IOException
	{
		FileWriter f = new FileWriter(fichier,true);

		f.write("for i=1,50 do\n");
		f.write("mlp:writeActivation(\"a_"+ fichier.getName() +"_0_\" .. i ,test[2][i])\n");
		f.write("end\n\n");

		f.write("criterion = nn.MSECriterion()\n");
		f.write("trainer = nn.StochasticGradient(mlp, criterion)\n");
		f.write("trainer.learningRate = "+learningRate+"\n");
		f.write("trainer:train2(dataset,\"ErreurQuadra"+fichier.getName()+".txt\")\n\n");

		f.write("mlp:writeWeightAndBias(\"WeightAndBias.txt\")\n\n");
		f.write("piad:writeClassif(\"Classif_"+ fichier.getName() +".txt\",mlp,inside2,outside2)\n");
		f.write("for i=1,50 do\n");
		f.write("mlp:writeActivation(\"a_"+ fichier.getName() +"_1_\" .. i ,test[2][i])\n");
		f.write("end\n");

		f.write("\n");
		f.close();

		return true;
	}

}
